﻿using System;
using System.Diagnostics;
using System.IO;
using StockTrader.Common;
using StockTrader.Core;
using StockTrader.Platform.Logging;
using StockTrader.Utilities;

// MD = MoreDebug
// DB = DataBase
// MC = MarketClosing
// SO = SquareOff
// EOD = End of Day
// EOP = End of Period

// SOMC-DB = SquareOff on MarketClosing, Runs on IntraDay Tick data and maintains EOD trading stats (profit/loss, num trades etc.) incl. EOP stats

namespace StockTrader.API.TradingAlgos
{
    public class AlgoScalper : TradingAlgo
    {

        public AlgoScalper(AlgoParams Params)
            : base(Params)
        {

        }

        public override void AddTick(SymbolTick si, bool? marketClosing)
        {
            DerivativeSymbolQuote di = si.D.Q;

            if (marketClosing == null)
                marketClosing = MarketUtils.IsTimeAfter325(di.UpdateTime);

            // di is the new tick here
            DTick tick = new DTick()
                            {
                                Di = di,
                                MarketDirection = MarketDirection.NONE,
                                PercChangeFromLastTrendingTick = 0,
                                Order = null,
                                TickNumber = TotalTickCount
                            };

            DerivativeSymbolQuote currentDi = tick.Di;

            // Stats
            double tradeValue = GetTradeValueForAnalysis(di);

            mTimeOfLastTick = tick.Di.QuoteTime;

            // First tick of this algo, initialize daytoday and other variables
            if (TotalTickCount == 1)
            {
                //MUNISHREMOVE -CHECK for a single tick only after mkt close, it is going in eodwindup and last tick is nul there
                lastTick = tick;

                //mAverageTradePrice = tradeValue;
                mTimeOfFirstTick = tick.Di.QuoteTime;
                DayToday = currentDi.QuoteTime;

                isEODWindupDone = false;
                EodTradeStats = new EODTradeStats(AlgoParams.R1, AlgoParams.R2);

                FileTracing.TraceVerbose("First tick of day. No analysis");

                if (MinTick == null || MaxTick == null)
                {
                    MinTick = MaxTick = tick;
                }
                TimeOfFirstTickOfDay = currentDi.QuoteTime;

                EopTradeStats.max_price = EopTradeStats.min_price = tradeValue;
                EodTradeStats.max_price = EodTradeStats.min_price = tradeValue;
            }
            mAverageTradePrice += tradeValue;

            FileTracing.TraceVerbose("New Tick. LTP = " + tradeValue + ". OFFER=" + di.BestOfferPriceDouble + ". BID=" +
                                     di.BestBidPriceDouble);

            isNextDay = (currentDi.UpdateTime.Date != DayToday.Date);

            bool doesNeedEODWindup = AlgoParams.IsMarketClosingSquareOff && (marketClosing.Value || isNextDay) && !isEODWindupDone;

            // if marketclosing then squareoff positions and dont take any new positions
            if (doesNeedEODWindup)
            {
                int numOpenPositions = TotalBuyTrades - TotalSellTrades;

                if (numOpenPositions != 0)
                {
                    // EOD Squareoff

                    bool isBuy = numOpenPositions < 0 ? true : false;

                    if (isBuy)
                        tick.MarketDirection = MarketDirection.UP;
                    else
                        tick.MarketDirection = MarketDirection.DOWN;

                    AnalyzeTickForOrder(tick, marketClosing.Value);
                }

                if (EodTradeStats == null)
                    EodTradeStats = new EODTradeStats(AlgoParams.R1, AlgoParams.R2);

                EodTradeStats.number_of_ticks = DayTotalTickConsideredCounts;

                EopTradeStats.number_of_ticks += EodTradeStats.number_of_ticks;

                //if(isNextDay)
                //{
                //    TimeOfLastTickOfDay = lastTick.Di.OriginalTime;
                //}
                //else
                //{
                //    TimeOfLastTickOfDay = currentDi.OriginalTime;
                //}
                //EopTradeStats

                EodTradeStats.inmarket_time_in_minutes = (int) (TimeOfLastTickOfDay - TimeOfFirstTickOfDay).TotalMinutes;

                EopTradeStats.inmarket_time_in_minutes += EodTradeStats.inmarket_time_in_minutes;

                EopTradeStats.num_days++;

                EodTradeStats.status_update_time = DateTime.Now;
                EodTradeStats.trade_date = DayToday.Date;
                EodTradeStats.algo_id = AlgoParams.AlgoId;
                EodTradeStats.contract_name = AlgoParams.I.Symbol;

                EodTradeStats.average_profit_pertrade = EodTradeStats.num_profit_trades == 0
                                                            ? 0
                                                            : EodTradeStats.average_profit_pertrade/
                                                              EodTradeStats.num_profit_trades;
                EodTradeStats.average_loss_pertrade = EodTradeStats.num_loss_trades == 0
                                                          ? 0
                                                          : EodTradeStats.average_loss_pertrade/
                                                            EodTradeStats.num_loss_trades;

                // Because money invested is only 25% (margin) for derivatives, thus multiplication factor of 4 by default
                EodTradeStats.roi_percentage = (EodTradeStats.expected_profit / lastTick.Di.LastTradedPriceDouble) * 100 * (1 / AlgoParams.MarginFraction);
                EodTradeStats.actual_roi_percentage = (EodTradeStats.actual_profit/lastTick.Di.LastTradedPriceDouble)*
                                                      100*(1/AlgoParams.MarginFraction);
                EodTradeStats.market_direction_percentage = AlgoParams.PercMarketDirectionChange;

                // Insert EodSummary into DB
                EodTradeStats.Persist();

                // Reset the variables for next day
                lastOrderSquareOffTriggerDirection = MarketDirection.NONE;
                DayTotalTickConsideredCounts = 0;
                MinTick = MaxTick = lastPeak = lastTriggerSquareOffTick = null;

                lastPeakType = MarketDirection.NONE;
                lastOrderSquareOffTriggerDirection = MarketDirection.NONE;
                //lastOrder = null;

                // Stats
                dayNett.Add(new DayNett(DayToday, TotalProfit));
                expectedDayNett.Add(new DayNett(DayToday, ExpectedNettProfit));

                isEODWindupDone = true;
            }

            // Will never hit in EOD data, because mIsMarketClosingSquareOff is false and hence isEODWindupDone is false always
            if (isNextDay && isEODWindupDone)
            {
                // Start of next day
                isEODWindupDone = false;
                DayToday = currentDi.QuoteTime;

                EodTradeStats = new EODTradeStats(AlgoParams.R1, AlgoParams.R2);
                EodTradeStats.max_price = EodTradeStats.min_price = tradeValue;

                FileTracing.TraceVerbose("First tick of day. No analysis");

                if (MinTick == null || MaxTick == null)
                    MinTick = MaxTick = tick;
                
                TimeOfFirstTickOfDay = currentDi.QuoteTime;
                TimeOfLastTickOfDay = currentDi.QuoteTime;
                DayTotalTickConsideredCounts++;
            }

            // Charting tricks
            if (isNextDay)
            {
                DayToday = currentDi.QuoteTime;
                lastTick = tick;
                DayChartTickCounter = 0;
            }
            else
            {
                DayChartTickCounter++;
            }

            //currentDi.UpdateTime += new TimeSpan(0, DayChartTickCounter*1, 0);

            // keep adding to global tick list
            //allTicks.Add(tick);

            // *********** //
            // After market hours, same day then windup must have been done, so return
            // windup is done on market closing hours or if ticks unavailable that day, then windup done on first tick of nextday
            if (!(!isNextDay && !isEODWindupDone) && AlgoParams.IsMarketClosingSquareOff)
                return;
            //*********** //

            // Analyzed ticks counting
            lastTick = tick;
            DayTotalTickConsideredCounts++;
            TimeOfLastTickOfDay = currentDi.QuoteTime;


            // Keeping track of Min and Max prices in the analyzed period
            if (tradeValue < EopTradeStats.min_price)
                EopTradeStats.min_price = tradeValue;
            else if (tradeValue > EopTradeStats.max_price)
                EopTradeStats.max_price = tradeValue;

            if (tradeValue < EodTradeStats.min_price)
                EodTradeStats.min_price = tradeValue;
            else if (tradeValue > EodTradeStats.max_price)
                EodTradeStats.max_price = tradeValue;

            // Peak Detection and Order Placement
            DerivativeSymbolQuote maxDi = MaxTick.Di;
            DerivativeSymbolQuote minDi = MinTick.Di;
            double percChangeFromMax = 100* ((GetTradeValueForAnalysis(di) - GetTradeValueForAnalysis(maxDi))/ GetTradeValueForAnalysis(maxDi));
            double percChangeFromMin = 100* ((GetTradeValueForAnalysis(di) - GetTradeValueForAnalysis(minDi))/ GetTradeValueForAnalysis(minDi));

            if (percChangeFromMax >= 0)
            {
                // New Max
                MaxTick = tick;
                tick.MarketDirection = MarketDirection.UP;
            }
            else if (percChangeFromMin <= 0)
            {
                // New Min
                MinTick = tick;
                tick.MarketDirection = MarketDirection.DOWN;
            }


            bool isPeakDetected = false;
            bool isSquareOffPointDetected = false;
            bool isSquareOff = false;

            if (AlgoParams.IsSingleTradePerDay)
            {
                //# Algo 7
                //if (EodTradeStats.num_trades == 1 && Math.Abs(E - TotalSellTrades) == 0)
                //    return;

                //# Algo 6
                //if (Math.Abs(TotalBuyTrades - TotalSellTrades) == 1)
                //    return;

                // # Algo 5
                //if (EodTradeStats.num_trades == AlgoParams.NumTradesStopForDay && Math.Abs(TotalBuyTrades - TotalSellTrades) == 0)
                //    return;
                //if (Math.Abs(TotalBuyTrades - TotalSellTrades) != 0)
                //    mPercChangeThreshold = AlgoParams.PercSquareOffThreshold;
                //else if (EodTradeStats.num_trades == 1 && Math.Abs(TotalBuyTrades - TotalSellTrades) == 0)
                //    return;
                //else
                //    mPercChangeThreshold = AlgoParams.PercMarketDirectionChange;
                //if (Math.Abs(TotalBuyTrades - TotalSellTrades) != 0)
                //    mPercChangeThreshold = AlgoParams.PercSquareOffThreshold;
                //else
                //    mPercChangeThreshold = AlgoParams.PercMarketDirectionChange;

                //# Algo 2
                if (EodTradeStats.num_trades == 1 && Math.Abs(TotalBuyTrades - TotalSellTrades) == 0)
                    return;
            }

            // ************** 1. CHECK TOP ********************** //

            // detect peaks and square off trigger points
            if (lastPeakType == MarketDirection.DOWN)
            {
                // Squareoff check
                if (AlgoParams.IsSquareOffTrigger && percChangeFromMax < 0 &&
                    Math.Abs(percChangeFromMax) >= AlgoParams.PercSquareOffThreshold)
                {
                    int numOpenPositions = Math.Abs(TotalBuyTrades - TotalSellTrades);

                    if (numOpenPositions != 0)
                    {
                        double orderPrice = tick.Di.BestBidPriceDouble;
                        double profitPoints = orderPrice - mOrders[0].OrderPrice;
                        double brokerage = (AlgoParams.SquareOffBrokerageFactor * AlgoParams.PercBrokerage * orderPrice / 100);

                        double nettProfit = profitPoints - brokerage;

                        double profitPerc = 100*(nettProfit/orderPrice);

                        if (!AlgoParams.IsMinProfitMust || profitPerc > AlgoParams.PercMinProfit)
                        {
                            // SquareOff point reached
                            tick.MarketDirection = MarketDirection.DOWN;
                            tick.PercChangeFromLastTrendingTick = percChangeFromMax;

                            isSquareOffPointDetected = true;
                            lastTriggerSquareOffTick = MaxTick;
                            lastOrderSquareOffTriggerDirection = tick.MarketDirection;
                            AnalyzeTickForOrder(tick, marketClosing.Value);
                        }
                    }
                }

                //TODO: canOrderThisAnalysis Shortcircuited to true
                //double percPriceDiff = (tick.Di.BidPriceDouble - lastOrder.OrderPrice) / lastOrder.OrderPrice;
                bool canOrderThisAnalysis = true;
                //(lastOrderSquareOffTriggerDirection != MarketDirection.DOWN) || (percPriceDiff > mPercBrokerage);

                if (percChangeFromMax < 0 && Math.Abs(percChangeFromMax) >= mPercChangeThreshold)
                {
                    // Record tick abberations
                    if (Math.Abs(percChangeFromMax) - AlgoParams.PercMarketDirectionChange > 1)
                    {
                        Logger.LogWarningMessage(
                            string.Format("{3}: {0} : PriceTick non-continuous {1}: PercChangeFromMax = {2} times.",
                                          tick.Di.QuoteTime, discontinuousMaxTickNum++,
                                          percChangeFromMax/AlgoParams.PercMarketDirectionChange, AlgoParams.I.Symbol));
                    }
                    // New top found
                    lastPeakType = MarketDirection.UP;
                    lastPeak = MaxTick;

                    tick.MarketDirection = MarketDirection.DOWN;
                    tick.PercChangeFromLastTrendingTick = percChangeFromMax;
                    MinTick = tick;

                    allPeaks.Add(TotalTickCount, lastPeak);
                    isPeakDetected = true;

                    if (canOrderThisAnalysis)
                    {
                        isSquareOff = AnalyzeTickForOrder(tick, marketClosing.Value);
                        lastOrderSquareOffTriggerDirection = MarketDirection.NONE;
                    }
                }
            }
                // 2. CHECK BOTTOM
            else if (lastPeakType == MarketDirection.UP)
            {
                // Look for squareoff
                if (AlgoParams.IsSquareOffTrigger && percChangeFromMin > 0 &&
                    Math.Abs(percChangeFromMin) >= AlgoParams.PercSquareOffThreshold)
                {
                    int numOpenPositions = Math.Abs(TotalBuyTrades - TotalSellTrades);

                    if (numOpenPositions != 0)
                    {
                        double orderPrice = tick.Di.BestOfferPriceDouble;
                        double profitPoints = orderPrice - mOrders[0].OrderPrice;
                        double brokerage = (AlgoParams.SquareOffBrokerageFactor * AlgoParams.PercBrokerage * orderPrice / 100);

                        double nettProfit = profitPoints - brokerage;

                        double profitPerc = (100 * nettProfit) / orderPrice;

                        if (!AlgoParams.IsMinProfitMust || profitPerc > AlgoParams.PercMinProfit)
                        {
                            // SquareOff point reached
                            tick.MarketDirection = MarketDirection.UP;
                            tick.PercChangeFromLastTrendingTick = percChangeFromMin;

                            isSquareOffPointDetected = true;
                            lastTriggerSquareOffTick = MinTick;
                            lastOrderSquareOffTriggerDirection = tick.MarketDirection;

                            AnalyzeTickForOrder(tick, marketClosing.Value);
                        }
                    }
                }

                //TODO: canOrderThisAnalysis Shortcircuited to true
                //double percPriceDiff = (lastOrder.OrderPrice - tick.Di.OfferPriceDouble) / lastOrder.OrderPrice;
                bool canOrderThisAnalysis = true;
                    // (lastOrderSquareOffTriggerDirection != MarketDirection.UP) || (percPriceDiff > mPercBrokerage);

                // Peak - New trades or pending squareoffs
                if (percChangeFromMin > 0 && Math.Abs(percChangeFromMin) >= mPercChangeThreshold)
                {
                    // Record tick abberations
                    if (Math.Abs(percChangeFromMin) - AlgoParams.PercMarketDirectionChange > 1)
                    {
                        Logger.LogWarningMessage(
                            string.Format("{3}: {0} : PriceTick non-continuous {1}: PercChangeFromMin = {2} times.",
                                          tick.Di.QuoteTime, discontinuousMinTickNum++,
                                          percChangeFromMin/AlgoParams.PercMarketDirectionChange, AlgoParams.I.Symbol));
                    }

                    // New bottom found
                    lastPeakType = MarketDirection.DOWN;
                    lastPeak = MinTick;

                    tick.MarketDirection = MarketDirection.UP;
                    tick.PercChangeFromLastTrendingTick = percChangeFromMin;
                    MaxTick = tick;


                    allPeaks.Add(TotalTickCount, lastPeak);
                    isPeakDetected = true;

                    if (canOrderThisAnalysis)
                    {
                        isSquareOff = AnalyzeTickForOrder(tick, marketClosing.Value);

                        lastOrderSquareOffTriggerDirection = MarketDirection.NONE;
                    }
                }
            }
                // 3. 1st PEAK (TOP/BOTTOM)
            else if (lastPeakType == MarketDirection.NONE)
            {
                // 1st peak
                double percDiffMinMax = 100*
                                        ((GetTradeValueForAnalysis(MaxTick.Di) - GetTradeValueForAnalysis(MinTick.Di))/
                                         GetTradeValueForAnalysis(MinTick.Di));

                if (Math.Abs(percDiffMinMax) >= mPercChangeThreshold)
                {
                    // Set last peak as reverse of current tick direction
                    lastPeakType = tick.MarketDirection == MarketDirection.UP
                                       ? MarketDirection.DOWN
                                       : MarketDirection.UP;

                    if (lastPeakType == MarketDirection.DOWN)
                    {
                        tick.PercChangeFromLastTrendingTick = percDiffMinMax;
                        lastPeak = MinTick;
                    }
                    else
                    {
                        tick.PercChangeFromLastTrendingTick = -percDiffMinMax;
                        lastPeak = MaxTick;
                    }
                    allPeaks.Add(TotalTickCount, lastPeak);
                    isPeakDetected = true;

                    isSquareOff = AnalyzeTickForOrder(tick, marketClosing.Value);
                }
            }

            // If last order was square offf in this tick, then take 1 more open position
            //if(mIsDoubleRide && isSquareOff)
            //    AnalyzeTickForOrder(tick, marketClosing.Value);

            //if (isSquareOffPointDetected)
            //{
            //    lastOrderSquareOffTriggerDirection = tick.MarketDirection;
            //}

            // Charting commented out for now

            //// do charting here
            //if (isPeakDetected)
            //{
            //    // chart out peak and order ticks
            //    // tick = order tick
            //    // allPeaks[allPeaks.Count-1] is peak tick

            //    DerivativeQuoteInformation peakDi = lastPeak.Di;
            //    bool isBottom = lastPeak.MarketDirection == MarketDirection.DOWN;

            //    var peakPoint = new ChartTickPoint(peakDi.UpdateTime, peakDi.LastTradedPriceDouble, isBottom, ChartTickType.Peak);
            //    _chartDelegate(peakPoint);

            //    bool isBuy = lastOrder.OrderPosition == Position.Buy;

            //    var orderPoint = new ChartTickPoint(tick.Di.UpdateTime, tick.Di.LastTradedPriceDouble, isBuy, ChartTickType.Order);
            //    _chartDelegate(orderPoint);
            //}


            //if (isSquareOffPointDetected)
            //{
            //    DerivativeQuoteInformation peakDi = lastTriggerSquareOffTick.Di;
            //    bool isBottom = lastPeak.MarketDirection == MarketDirection.DOWN;

            //    var peakPoint = new ChartTickPoint(peakDi.UpdateTime, peakDi.LastTradedPriceDouble, isBottom, ChartTickType.SquareOffPoint);
            //    _chartDelegate(peakPoint);

            //    bool isBuy = lastOrder.OrderPosition == Position.Buy;

            //    var orderPoint = new ChartTickPoint(tick.Di.UpdateTime, tick.Di.LastTradedPriceDouble, isBuy, ChartTickType.Order);
            //    _chartDelegate(orderPoint);

            //}

            //{
            //    // chart out the tick
            //    // tick = normal tick
            //    var pricePoint = new ChartTickPoint(tick.Di.UpdateTime, tick.Di.LastTradedPriceDouble, false, ChartTickType.Price);
            //    //addDataDel(point);
            //    //dt = dt.AddMinutes(x);
            //    _chartDelegate(pricePoint);
            //    //mChart.AddTick(tick.Di.UpdateTime, tick.Di.LastTradedPriceDouble);
            //    //AddTickToChart(tick.Di.UpdateTime, tick.Di.LastTradedPriceDouble);
            //}
            //mChart.Visible = true;
        }

        protected override bool AnalyzeTickForOrder(DTick tick, bool marketClosing)
        {
            DerivativeSymbolQuote di = tick.Di;
            bool isBuy = tick.MarketDirection == MarketDirection.UP;

            // Just Reverse the direction
            //isBuy = !isBuy;

            //double orderPrice = isBuy ? di.OfferPriceDoublH: di.BidPriceDouble;
            //double percPriceDiff = (orderPrice - lastOrder.OrderPrice) / lastOrder.OrderPrice;

            //bool canOrderThisAnalysis = !wasLastOrderSquareOffTriggered || (percPriceDiff > mPercBrokerage);

            int numOpenPositions = Math.Abs(TotalBuyTrades - TotalSellTrades);

            bool isMoreBuyOpenPositionsThanSell = TotalBuyTrades > TotalSellTrades;

            bool isSquareOff =
                !((isMoreBuyOpenPositionsThanSell && isBuy) || (!isMoreBuyOpenPositionsThanSell && !isBuy));

            if (numOpenPositions == 0)
                isSquareOff = false;

            bool isLimitAvailable = (numOpenPositions < AlgoParams.MaxTotalPositions);

            // # Algo 2 - stop loss after N trades if in loss or certain daily loss limit
            if (AlgoParams.IsLimitTradesPerDay)
            {
                if (!isSquareOff)
                {
                    if ((EodTradeStats.actual_profit < 0 &&
                         Math.Abs((EodTradeStats.actual_profit*100)/ EodTradeStats.min_price) >= AlgoParams.PercPnLStopForDay)
                        || (EodTradeStats.actual_profit < 0 && EodTradeStats.num_trades >= AlgoParams.NumTradesStopForDay))
                    {
                        //DoStopAlgo = true;
                        return false;
                    }
                }
            }

            //if (!canOrderThisAnalysis)
            //    return isSquareOff;

            //bool isExtraTrade = isBuy && isMoreBuyOpenPositions;


            //if (isBuy && isMoreBuyOpenPositions)
            //{
            //    // Apply LongCeiling price filter
            //    if (mLongCeilingPrice > 0 && orderPrice > mLongCeilingPrice)
            //    {
            //        FileTracing.TraceVerbose("FreshLong: Discarding : LongCeilingPrice hit");
            //        orderValid = false;
            //    }
            //}
            //else if (!isBuy && !isMoreBuyOpenPositions)
            //{


            //}



            if (isSquareOff || isLimitAvailable)
            {
                var orderDirection = isBuy ? Position.Buy : Position.Sell;
                var newOrder = new StockOrder(orderDirection, 0);
                if (PlaceOrder(newOrder, di))
                {
                    TotalTurnover += newOrder.OrderPrice * AlgoParams.I.Qty;
                    if (newOrder.OrderPosition == Position.Buy)
                    {
                        TotalBuyTrades++;
                        EodTradeStats.num_trades++;
                        FileTracing.TraceTradeInformation("Trade Confirmed at " + newOrder.OrderPrice + ": Order Reference Number = " + newOrder.OrderRef);
                    }
                    else if (newOrder.OrderPosition == Position.Sell)
                    {
                        TotalSellTrades++;
                        FileTracing.TraceTradeInformation("Trade Confirmed at " + newOrder.OrderPrice + ": Order Reference Number = " + newOrder.OrderRef);
                    }

                    if (marketClosing)
                    {
                        newOrder.ExpectedOrderPrice = newOrder.OrderPrice;
                    }
                    else
                    {
                        double exactThreshold;
                        double expectedTradePrice = lastPeak.Di.LastTradedPriceDouble;

                        // Ideal conditions Order price setting. Not taking Bid/Ask price exactly (in data with us they are unavailable)
                        // just using LTP
                        if (newOrder.OrderPosition == Position.Buy)
                        {
                            exactThreshold = (1 + mPercChangeThreshold/100);
                        }
                        else
                        {
                            exactThreshold = (1 - mPercChangeThreshold/100);
                        }
                        // On next day, market opens afresh (may be highly different from previous  close (previous tick)) 
                        // so order price will be same as LTP
                        // but only when continuous algo (algo id 11) functions, because for SquareOff on marketClosing algo,
                        // anyway the squareoff was done and 1st trade will neevr be on 1st tick of nextDay
                        // in algo9 SO on 1st tick of next day is done only if previous day data was incomplete or something
                        if (isNextDay && !AlgoParams.IsMarketClosingSquareOff)
                        {
                            exactThreshold = 1;
                            expectedTradePrice = di.LastTradedPriceDouble;
                        }
                        newOrder.ExpectedOrderPrice = expectedTradePrice*exactThreshold;
                    }

                    FileTracing.TraceVerbose("AnalyzeTickForOrder: PlaceOrder Successful");

                    StockOrder squaredOffOrder = null;

                    // Get paired order
                    for (int i = mOrders.Count - 1; i >= 0; i--)
                    {
                        if (mOrders[i].OrderPosition != newOrder.OrderPosition)
                        {
                            squaredOffOrder = mOrders[i];
                            mOrders.RemoveAt(i);
                            break;
                        }
                    }

                    if (squaredOffOrder != null)
                    {
                        double profitPoints = squaredOffOrder.OrderPrice - newOrder.OrderPrice;
                        if (!isBuy)
                        {
                            profitPoints = -profitPoints;
                        }

                        if (profitPoints >= 0)
                        {
                            profitableOrders.Add(profitPoints);
                        }


                        double brokerage = (AlgoParams.SquareOffBrokerageFactor * AlgoParams.PercBrokerage * newOrder.OrderPrice / 100);

                        double nettProfit = profitPoints - brokerage;

                        if (nettProfit >= 0)
                        {
                            profitableAfterBrokerageOrders.Add(nettProfit);
                        }
                        else
                        {
                            lossOrders.Add(nettProfit);
                        }

                        TotalProfit += nettProfit;
                        TotalBrokerage += brokerage;

                        EodTradeStats.actual_profit += nettProfit;
                        EodTradeStats.brokerage += brokerage;

                        FileTracing.TraceTradeInformation("SquareOff: Profit Amount (after brokerage) = " + profitPoints);

                        // Ideal conditions expected nettProfit
                        // Expected loss should be threshold percent at max in ideal conditions.
                        //double expectedProfit = -profitPoints - brokerage - (newOrder.OrderPrice * AlgoParams.PercMarketDirectionChange);
                        double expectedProfitPoints = squaredOffOrder.ExpectedOrderPrice - newOrder.ExpectedOrderPrice;
                        if (!isBuy)
                        {
                            expectedProfitPoints = -expectedProfitPoints;
                        }

                        double expectedNettProfit = expectedProfitPoints - brokerage;

                        ExpectedNettProfit += expectedNettProfit;

                        // Expected profit should always be greater. If not, then either there is a bug in code or tick data is incorrect.
                        Debug.Assert(nettProfit - expectedNettProfit <= 0);

                        if (expectedNettProfit < 0)
                        {
                            //if (EodTradeStats.num_loss_trades == 0)
                            EodTradeStats.average_loss_pertrade += expectedNettProfit;
                            EodTradeStats.num_loss_trades++;

                            EopTradeStats.average_loss_pertrade += expectedNettProfit;
                            EopTradeStats.num_loss_trades++;
                            //EodTradeStats.average_loss_pertrade = (EodTradeStats.average_loss_pertrade + expectedNettProfit) / 2;
                        }
                        else if (expectedNettProfit > 0)
                        {
                            //if (EodTradeStats.num_profit_trades == 0)
                            EodTradeStats.average_profit_pertrade += expectedNettProfit;
                            EodTradeStats.num_profit_trades++;

                            EopTradeStats.average_profit_pertrade += expectedNettProfit;
                            EopTradeStats.num_profit_trades++;

                            //EodTradeStats.average_profit_pertrade = (EodTradeStats.average_profit_pertrade + expectedNettProfit) / 2;
                        }

                        EodTradeStats.expected_profit += expectedNettProfit;

                    }
                    else
                    {
                        mOrders.Add(newOrder);
                    }

                    // Add to Global orders list
                    allOrders.Add(newOrder);

                    //allPeaks[TotalTickCount].Order = newOrder;

                    lastOrder = newOrder;
                }
                else
                {
                    FileTracing.TraceVerbose("Analyze: PlaceOrder failed due to IOC");
                }
            }
            else
            {
                TotalTimesLimitShortage++;
                FileTracing.TraceInformation("Not sufficient limit to place order: Number " + TotalTimesLimitShortage);
            }

            // Every peak should have an associated order
            if (!AlgoParams.IsMarketClosingSquareOff)
                Debug.Assert(allPeaks.Count == allOrders.Count);

            return isSquareOff;
        }
    }
}
